
(defclass field ()
  ((width  :reader width  :initarg :width)
   (depth  :reader depth  :initarg :depth)
   (grid   :reader grid   :initform nil)
   (houses :reader houses :initform nil)))

(defmethod initialize-instance :after ((field field) &key) 
  (setf (slot-value field 'grid)
        (make-array (list (width field) (depth field)) :initial-element 'empty)))

(defclass house () 
  ((vrijstand :initarg :vrijstand :initform nil)))
(defclass maison (house) ())
(defclass bungalow (house) ())
(defclass eengezinswoning (house) ())
(defclass placement ()
  ((x     :reader x     :initarg :x)
   (y     :reader y     :initarg :y)
   (house :reader house :initarg :house)))

(defgeneric width (house)
  (:method ((maison maison)) 22)
  (:method ((bungalow bungalow)) 20)
  (:method ((eengezinswoning eengezinswoning)) 16)
  (:method ((house house)) 0))

(defgeneric depth (house)
  (:method ((maison maison)) 21)
  (:method ((bungalow bungalow)) 15)
  (:method ((eengezinswoning eengezinswoning)) 16)
  (:method ((house house)) 0))

(defgeneric required-vrijstand (house)
  (:method ((maison maison)) 12)
  (:method ((bungalow bungalow)) 6)
  (:method ((eengezinswoning eengezinswoning)) 4)
  (:method ((house house)) 0))

(defgeneric vrijstand (house)
  (:method ((house house))
    (or (slot-value house 'vrijstand) (required-vrijstand house))))

(defgeneric percentual-increase-vrijstand (house)
  (:method ((maison maison)) 0.06)
  (:method ((bungalow bungalow)) 0.04)
  (:method ((eengezinswoning eengezinswoning)) 0.03)
  (:method ((house house)) 0))

(defgeneric normal-value (house)
  (:method ((maison maison)) 610000)
  (:method ((bungalow bungalow)) 399000)
  (:method ((eengezinswoning eengezinswoning)) 285000)
  (:method ((house house)) 0))

(defgeneric value (house)
  (:method ((house house))
    (* (normal-value house) 
       (+ 1.0
          (* (percentual-increase-vrijstand house)
             (floor (/ (- (vrijstand house) (required-vrijstand house))
                       2))))))
  (:method ((field field))
    (reduce #'+ (mapcar #'value (houses field)))))
